package org.geekbang.projects.config;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigValue;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.eclipse.microprofile.config.spi.Converter;
import org.geekbang.projects.config.converter.Converters;
import org.geekbang.projects.config.converter.MatchableConverter;
import org.geekbang.projects.config.source.ConfigSources;
import org.geektimes.base.util.ReflectionUtils;
import org.geektimes.base.util.TypeUtils;

import java.util.*;

/**
 * 通用配置
 *
 * @author : KangNing Hu
 */
public class CommonConfig implements Config {


	/**
	 * 配置源集
	 */
	private ConfigSources  configSources;


	/**
	 * 转换器集
	 */
	private Converters converters;




	public CommonConfig(ConfigSources configSources, Converters converters) {
		this.configSources = configSources;
		this.converters = converters;
	}



	@Override
	public <T> T getValue(String name, Class<T> aClass) {
		ConfigSource configSource = getConfigSource(name);
		return configSource != null ? getUsableConverter(aClass).convert(configSource.getValue(name)) : null;
	}

	@Override
	public ConfigValue getConfigValue(String name) {
		ConfigSource configSource = getConfigSource(name);
		return configSource != null ? new CommonConfigValue(configSource, name) : null;
	}


	@Override
	public <T> Optional<T> getOptionalValue(String name, Class<T> aClass) {
		return Optional.ofNullable(getValue(name, aClass));
	}

	@Override
	public Iterable<String> getPropertyNames() {
		Iterable<ConfigSource> configSources = getConfigSources();
		Set<String> propertyNames = new LinkedHashSet<>();
		configSources.forEach(item -> propertyNames.addAll(item.getPropertyNames()));
		return propertyNames;
	}

	@Override
	public Iterable<ConfigSource> getConfigSources() {
		return Collections.unmodifiableCollection(this.configSources.sort());
	}


	@Override
	public <T> Optional<Converter<T>> getConverter(Class<T> aClass) {
		return Optional.<Converter<T>>of(getUsableConverter(aClass));
	}


	@Override
	public <T> T unwrap(Class<T> aClass) {
		return ReflectionUtils.newInstance(aClass);
	}


	/**
	 * 获取配置源
	 *
	 * @param name
	 * @return
	 */
	private ConfigSource getConfigSource(String name) {
		Iterable<ConfigSource> configSources = getConfigSources();
		for (ConfigSource configSource : configSources) {
			if (configSource.getPropertyNames().contains(name)) {
				return configSource;
			}
		}
		return null;
	}


	/**
	 * 获取一个可用的转换器
	 * @param type
	 * @param <T>
	 * @return
	 */
	private <T> Converter<T> getUsableConverter(Class<T> type){
		for (Converter converter : converters){
			if (converter instanceof MatchableConverter && ((MatchableConverter) converter).matching(type)){
				return converter;
			}
		}
		if (!converters.isEmpty()){
			return converters.get(0);
		}
		return value -> TypeUtils.transform(value, type);
	}




}
